home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / alpha.arc / FTPSERV.C < prev    next >
C/C++ Source or Header  |  1988-03-22  |  18KB  |  706 lines

  1. /* FTP Server state machine - see RFC 959 */
  2.  
  3. #define    LINELEN        128    /* Length of command buffer */
  4.  
  5. #include <stdio.h>
  6. #include "global.h"
  7. #include "mbuf.h"
  8. #include "netuser.h"
  9. #include "timer.h"
  10. #include "tcp.h"
  11. #include "ftp.h"
  12.  
  13. /* Command table */
  14. static char *commands[] = {
  15.     "user",
  16. #define    USER_CMD    0
  17.     "acct",
  18. #define    ACCT_CMD    1
  19.     "pass",
  20. #define    PASS_CMD    2
  21.     "type",
  22. #define    TYPE_CMD    3
  23.     "list",
  24. #define    LIST_CMD    4
  25.     "cwd",
  26. #define    CWD_CMD        5
  27.     "dele",
  28. #define    DELE_CMD    6
  29.     "name",
  30. #define    NAME_CMD    7
  31.     "quit",
  32. #define    QUIT_CMD    8
  33.     "retr",
  34. #define    RETR_CMD    9
  35.     "stor",
  36. #define    STOR_CMD    10
  37.     "port",
  38. #define    PORT_CMD    11
  39.     "nlst",
  40. #define    NLST_CMD    12
  41.     "pwd",
  42. #define    PWD_CMD        13
  43.     "xpwd",            /* For compatibility with 4.2BSD */
  44. #define    XPWD_CMD    14
  45.     "mkd ",
  46. #define    MKD_CMD        15
  47.     "xmkd",            /* For compatibility with 4.2BSD */
  48. #define    XMKD_CMD    16
  49.     "xrmd",            /* For compatibility with 4.2BSD */
  50. #define    XRMD_CMD    17
  51.     "rmd ",
  52. #define    RMD_CMD        18
  53.     "stru",
  54. #define    STRU_CMD    19
  55.     "mode",
  56. #define    MODE_CMD    20
  57.     NULLCHAR
  58. };
  59.  
  60. /* Response messages */
  61. static char banner[] = "220 %s FTP version %s ready at %s\r\n";
  62. static char badcmd[] = "500 Unknown command\r\n";
  63. static char unsupp[] = "500 Unsupported command or option\r\n";
  64. static char givepass[] = "331 Enter PASS command\r\n";
  65. static char logged[] = "230 Logged in\r\n";
  66. static char typeok[] = "200 Type OK\r\n";
  67. static char only8[] = "501 Only logical bytesize 8 supported\r\n";
  68. static char deleok[] = "250 File deleted\r\n";
  69. static char mkdok[] = "200 MKD ok\r\n";
  70. static char delefail[] = "550 Delete failed\r\n";
  71. static char pwdmsg[] = "257 \"%s\" is current directory\r\n";
  72. static char badtype[] = "501 Unknown type \"%s\"\r\n";
  73. static char badport[] = "501 Bad port syntax\r\n";
  74. static char unimp[] = "502 Command not yet implemented\r\n";
  75. static char bye[] = "221 Goodbye!\r\n";
  76. static char nodir[] = "553 Can't read directory \"%s\"\r\n";
  77. static char cantopen[] = "550 Can't read file \"%s\"\r\n";
  78. static char sending[] = "150 Opening data connection for %s %s\r\n";
  79. static char cantmake[] = "553 Can't create \"%s\"\r\n";
  80. static char portok[] = "200 Port command okay\r\n";
  81. static char rxok[] = "226 File received OK\r\n";
  82. static char txok[] = "226 File sent OK\r\n";
  83. static char noperm[] = "550 Permission denied\r\n";
  84. static char noconn[] = "425 Data connection reset\r\n";
  85. static char notlog[] = "530 Please log in with USER and PASS\r\n";
  86. static char okay[] = "200 Ok\r\n";
  87.  
  88. static struct tcb *ftp_tcb;
  89.  
  90. /* Start up FTP service */
  91. ftp1(argc,argv)
  92. int argc;
  93. char *argv[];
  94. {
  95.     struct socket lsocket;
  96.     void ftpscr(),ftpscs();
  97.  
  98.     lsocket.address = ip_addr;
  99.     if(argc < 2)
  100.         lsocket.port = FTP_PORT;
  101.     else
  102.         lsocket.port = atoi(argv[1]);
  103.  
  104.     ftp_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,ftpscr,NULLVFP,ftpscs,0,(char *)NULL);
  105. }
  106. /* Shut down FTP server */
  107. ftp0()
  108. {
  109.     if(ftp_tcb != NULLTCB)
  110.         close_tcp(ftp_tcb);
  111. }
  112. /* FTP Server Control channel State change upcall handler */
  113. static
  114. void
  115. ftpscs(tcb,old,new)
  116. struct tcb *tcb;
  117. char old,new;
  118. {
  119.     extern char hostname[],version[];
  120.     struct ftp *ftp,*ftp_create();
  121.     void ftp_delete();
  122.     char *inet_ntoa();
  123.     long t;
  124.     char *cp,*cp1;
  125.  
  126.     switch(new){
  127. /* Setting QUICKSTART piggybacks the server's banner on the SYN/ACK segment;
  128.  * leaving it unset waits for the three-way handshake to complete before
  129.  * sending the banner. Piggybacking unfortunately breaks some old TCPs,
  130.  * so its use is not (yet) recommended.
  131. */
  132. #ifdef    QUICKSTART
  133.     case SYN_RECEIVED:
  134. #else
  135.     case ESTABLISHED:
  136. #endif
  137.         if((ftp = ftp_create(LINELEN)) == NULLFTP){
  138.             /* No space, kill connection */
  139.             close_tcp(tcb);
  140.             return;
  141.         }
  142.         ftp->control = tcb;        /* Downward link */
  143.         tcb->user = (char *)ftp;    /* Upward link */
  144.  
  145.         /* Set default data port */
  146.         ftp->port.address = tcb->conn.remote.address;
  147.         ftp->port.port = FTPD_PORT;
  148.  
  149.         /* Note current directory */
  150.         log(tcb,"open FTP");
  151.         time(&t);
  152.         cp = ctime(&t);
  153.         if((cp1 = index(cp,'\n')) != NULLCHAR)
  154.             *cp1 = '\0';
  155.         tprintf(ftp->control,banner,hostname,version,cp);
  156.         break;        
  157.     case CLOSE_WAIT:
  158.         close_tcp(tcb);
  159.         break;
  160.     case CLOSED:
  161.         log(tcb,"close FTP");
  162.         if((ftp = (struct ftp *)tcb->user) != NULLFTP)
  163.             ftp_delete(ftp);
  164.         /* Check if server is being shut down */
  165.         if(tcb == ftp_tcb)
  166.             ftp_tcb = NULLTCB;
  167.         del_tcp(tcb);
  168.         break;
  169.     }
  170. }
  171.  
  172. /* FTP Server Control channel Receiver upcall handler */
  173. static
  174. void
  175. ftpscr(tcb,cnt)
  176. struct tcb *tcb;
  177. int16 cnt;
  178. {
  179.     register struct ftp *ftp;
  180.     char c;
  181.     struct mbuf *bp;
  182.     void ftpcommand();
  183.  
  184.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  185.         /* Unknown connection, just kill it */
  186.         close_tcp(tcb);
  187.         return;
  188.     }
  189.     switch(ftp->state){
  190.     case COMMAND_STATE:
  191.         /* Assemble an input line in the session buffer. Return if incomplete */
  192.         recv_tcp(tcb,&bp,0);
  193.         while(pullup(&bp,&c,1) == 1){
  194.             switch(c){
  195.             case '\r':    /* Strip cr's */
  196.                 continue;
  197.             case '\n':    /* Complete line; process it */
  198.                 ftp->buf[ftp->cnt] = '\0';
  199.                 ftpcommand(ftp);
  200.                 ftp->cnt = 0;
  201.                 break;
  202.             default:    /* Assemble line */
  203.                 if(ftp->cnt != LINELEN-1)
  204.                     ftp->buf[ftp->cnt++] = c;
  205.                 break;
  206.             }
  207.         }
  208.         /* else no linefeed present yet to terminate command */
  209.         break;
  210.     case SENDING_STATE:
  211.     case RECEIVING_STATE:
  212.         /* Leave commands pending on receive queue until
  213.          * present command is done
  214.          */
  215.         break;
  216.     }
  217. }
  218.  
  219. /* FTP server data channel connection state change upcall handler */
  220. void
  221. ftpsds(tcb,old,new)
  222. struct tcb *tcb;
  223. char old,new;
  224. {
  225.     register struct ftp *ftp;
  226.  
  227.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  228.         /* Unknown connection. Kill it */
  229.         del_tcp(tcb);
  230.     } else if((old == FINWAIT1 || old == CLOSING) && ftp->state == SENDING_STATE){
  231.         /* We've received an ack of our FIN while sending; we're done */
  232.         ftp->state = COMMAND_STATE;
  233.         tprintf(ftp->control,txok);
  234.         /* Kick command parser if something is waiting */
  235.         if(ftp->control->rcvcnt != 0)
  236.             ftpscr(ftp->control,ftp->control->rcvcnt);
  237.     } else if(ftp->state == RECEIVING_STATE && new == CLOSE_WAIT){
  238.         /* FIN received on incoming file */
  239. #ifdef    CPM
  240.         if(ftp->type == ASCII_TYPE)
  241.             putc(CTLZ,ftp->fp);
  242. #endif
  243.         close_tcp(tcb);
  244.         if(ftp->fp != stdout)
  245.             fclose(ftp->fp);
  246.         ftp->fp = NULLFILE;
  247.         ftp->state = COMMAND_STATE;
  248.         tprintf(ftp->control,rxok);
  249.         /* Kick command parser if something is waiting */
  250.         if(ftp->control->rcvcnt != 0)
  251.             ftpscr(ftp->control,ftp->control->rcvcnt);
  252.     } else if(new == CLOSED){
  253.         if(tcb->reason != NORMAL){
  254.             /* Data connection was reset, complain about it */
  255.             tprintf(ftp->control,noconn);
  256.             /* And clean up */
  257.             if(ftp->fp != NULLFILE && ftp->fp != stdout)
  258.                 fclose(ftp->fp);
  259.             ftp->fp = NULLFILE;
  260.             ftp->state = COMMAND_STATE;
  261.             /* Kick command parser if something is waiting */
  262.             if(ftp->control->rcvcnt != 0)
  263.                 ftpscr(ftp->control,ftp->control->rcvcnt);
  264.         }
  265.         /* Clear only if another transfer hasn't already started */
  266.         if(ftp->data == tcb)
  267.             ftp->data = NULLTCB;
  268.         del_tcp(tcb);
  269.     }
  270. }
  271.  
  272. /* Parse and execute ftp commands */
  273. static
  274. void
  275. ftpcommand(ftp)
  276. register struct ftp *ftp;
  277. {
  278.     void ftpdr(),ftpdt(),ftpsds();
  279.     char *cmd,*arg,*cp,**cmdp,*file;
  280.     char *pathname();
  281.     char *mode;
  282.     struct socket dport;
  283.     
  284. #ifndef    CPM
  285.     FILE *dir();
  286. #endif
  287.  
  288.     cmd = ftp->buf;
  289.     if(ftp->cnt == 0){
  290.         /* Can't be a legal FTP command */
  291.         tprintf(ftp->control,badcmd);
  292.         return;
  293.     }    
  294.     cmd = ftp->buf;
  295.  
  296. #ifdef    UNIX
  297.     /* Translate first word to lower case */
  298.     for(cp = cmd;*cp != ' ' && *cp != '\0';cp++)
  299.         *cp = tolower(*cp);
  300. #else
  301.     /* Translate entire buffer to lower case */
  302.     for(cp = cmd;*cp != '\0';cp++)
  303.         *cp = tolower(*cp);
  304. #endif
  305.     /* Find command in table; if not present, return syntax error */
  306.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  307.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  308.             break;
  309.     if(*cmdp == NULLCHAR){
  310.         tprintf(ftp->control,badcmd);
  311.         return;
  312.     }
  313.     /* Allow only USER, PASS and QUIT before logging in */
  314.     if(ftp->cd == NULLCHAR || ftp->path == NULLCHAR){
  315.         switch(cmdp-commands){
  316.         case USER_CMD:
  317.         case PASS_CMD:
  318.         case QUIT_CMD:
  319.             break;
  320.         default:
  321.             tprintf(ftp->control,notlog);
  322.             return;
  323.         }
  324.     }
  325.     arg = &cmd[strlen(*cmdp)];
  326.     while(*arg == ' ')
  327.         arg++;
  328.  
  329.     /* Execute specific command */
  330.     switch(cmdp-commands){
  331.     case USER_CMD:
  332.         if((ftp->username = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  333.             close_tcp(ftp->control);
  334.             break;
  335.         }
  336.         strcpy(ftp->username,arg);
  337.         tprintf(ftp->control,givepass);
  338.         break;
  339.     case TYPE_CMD:
  340.         switch(arg[0]){
  341.         case 'A':
  342.         case 'a':    /* Ascii */
  343.             ftp->type = ASCII_TYPE;
  344.             tprintf(ftp->control,typeok);
  345.             break;
  346.         case 'l':
  347.         case 'L':
  348.             while(*arg != ' ' && *arg != '\0')
  349.                 arg++;
  350.             if(*arg == '\0' || *++arg != '8'){
  351.                 tprintf(ftp->control,only8);
  352.                 break;
  353.             }    /* Note fall-thru */
  354.         case 'B':
  355.         case 'b':    /* Binary */
  356.         case 'I':
  357.         case 'i':    /* Image */
  358.             ftp->type = IMAGE_TYPE;
  359.             tprintf(ftp->control,typeok);
  360.             break;
  361.         default:    /* Invalid */
  362.             tprintf(ftp->control,badtype,arg);
  363.             break;
  364.         }
  365.         break;
  366.     case QUIT_CMD:
  367.         tprintf(ftp->control,bye);
  368.         close_tcp(ftp->control);
  369.         break;
  370.     case RETR_CMD:
  371.         /* Disk operation; return ACK now */
  372.         tcp_output(ftp->control);
  373.         file = pathname(ftp->cd,arg);
  374.         if(ftp->type == IMAGE_TYPE)
  375.             mode = binmode[READ_BINARY];
  376.         else
  377.             mode = "r";
  378.         if(!permcheck(ftp,RETR_CMD,file)){
  379.              tprintf(ftp->control,noperm);
  380.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  381.             tprintf(ftp->control,cantopen,file);
  382.         } else {
  383.             log(ftp->control,"RETR %s/%s",ftp->cd,arg);
  384.             dport.address = ip_addr;
  385.             dport.port = FTPD_PORT;
  386.             ftp->state = SENDING_STATE;
  387.             tprintf(ftp->control,sending,"RETR",arg);
  388.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  389.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  390.         }
  391.         free(file);
  392.         break;
  393.     case STOR_CMD:
  394.         /* Disk operation; return ACK now */
  395.         tcp_output(ftp->control);
  396.         file = pathname(ftp->cd,arg);
  397.         if(ftp->type == IMAGE_TYPE)
  398.             mode = binmode[WRITE_BINARY];
  399.         else
  400.             mode = "w";
  401.         if(!permcheck(ftp,STOR_CMD,file)){
  402.              tprintf(ftp->control,noperm);
  403.             free(file);
  404.              break;
  405.         } else if((ftp->fp = fopen(file,mode)) == NULLFILE){
  406.             tprintf(ftp->control,cantmake,file);
  407.         } else {
  408.             log(ftp->control,"STOR %s/%s",ftp->cd,arg);
  409.             dport.address = ip_addr;
  410.             dport.port = FTPD_PORT;
  411.             ftp->state = RECEIVING_STATE;
  412.             tprintf(ftp->control,sending,"STOR",arg);
  413.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  414.              0,ftpdr,NULLVFP,ftpsds,ftp->control->tos,(char *)ftp);
  415.         }
  416.         free(file);
  417.         break;
  418.     case PORT_CMD:
  419.         if(pport(&ftp->port,arg) == -1){
  420.             tprintf(ftp->control,badport);
  421.         } else {
  422.             tprintf(ftp->control,portok);
  423.         }
  424.         break;
  425. #ifndef CPM
  426.     case LIST_CMD:
  427.         /* Disk operation; return ACK now */
  428.         tcp_output(ftp->control);
  429.  
  430.         file = pathname(ftp->cd,arg);
  431.         if(!permcheck(ftp,RETR_CMD,file)){
  432.              tprintf(ftp->control,noperm);
  433.         } else if((ftp->fp = dir(file,1)) == NULLFILE){
  434.             tprintf(ftp->control,nodir,file);
  435.         } else {
  436.             dport.address = ip_addr;
  437.             dport.port = FTPD_PORT;
  438.             ftp->state = SENDING_STATE;
  439.             tprintf(ftp->control,sending,"LIST",file);
  440.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  441.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  442.         }
  443.         free(file);
  444.         break;
  445.     case NLST_CMD:
  446.         /* Disk operation; return ACK now */
  447.         tcp_output(ftp->control);
  448.  
  449.         file = pathname(ftp->cd,arg);
  450.         if(!permcheck(ftp,RETR_CMD,file)){
  451.              tprintf(ftp->control,noperm);
  452.         } else if((ftp->fp = dir(file,0)) == NULLFILE){
  453.             tprintf(ftp->control,nodir,file);
  454.         } else {
  455.             dport.address = ip_addr;
  456.             dport.port = FTPD_PORT;
  457.             ftp->state = SENDING_STATE;
  458.             tprintf(ftp->control,sending,"NLST",file);
  459.             ftp->data = open_tcp(&dport,&ftp->port,TCP_ACTIVE,
  460.              0,NULLVFP,ftpdt,ftpsds,ftp->control->tos,(char *)ftp);
  461.         }
  462.         free(file);
  463.         break;
  464.     case CWD_CMD:
  465.         tcp_output(ftp->control);    /* Disk operation; return ACK now */
  466.  
  467.         file = pathname(ftp->cd,arg);
  468.         if(!permcheck(ftp,RETR_CMD,file)){
  469.              tprintf(ftp->control,noperm);
  470.             free(file);
  471. #ifdef    MSDOS
  472.         /* Don'tcha just LOVE %%$#@!! MS-DOS? */
  473.         } else if(strcmp(file,"\\") == 0 || access(file,0) == 0){
  474. #else
  475.         } else if(access(file,0) == 0){    /* See if it exists */
  476. #endif
  477.             /* Succeeded, record in control block */
  478.             free(ftp->cd);
  479.             ftp->cd = file;
  480.             tprintf(ftp->control,pwdmsg,file);
  481.         } else {
  482.             /* Failed, don't change anything */
  483.             tprintf(ftp->control,nodir,file);
  484.             free(file);
  485.         }
  486.         break;
  487.     case XPWD_CMD:
  488.     case PWD_CMD:
  489.         tprintf(ftp->control,pwdmsg,ftp->cd);
  490.         break;
  491. #else
  492.     case LIST_CMD:
  493.     case NLST_CMD:
  494.     case CWD_CMD:
  495.     case XPWD_CMD:
  496.     case PWD_CMD:
  497. #endif
  498.     case ACCT_CMD:        
  499.         tprintf(ftp->control,unimp);
  500.         break;
  501.     case DELE_CMD:
  502.         file = pathname(ftp->cd,arg);
  503.         if(!permcheck(ftp,DELE_CMD,file)){
  504.              tprintf(ftp->control,noperm);
  505.         } else if(unlink(file) == 0){
  506.             tprintf(ftp->control,deleok);
  507.         } else {
  508.             tprintf(ftp->control,delefail);
  509.         }
  510.         free(file);
  511.         break;
  512.     case PASS_CMD:
  513.         tcp_output(ftp->control);    /* Send the ack now */
  514.         ftplogin(ftp,arg);            
  515.         break;
  516. #ifndef    CPM
  517.     case XMKD_CMD:
  518.     case MKD_CMD:
  519.         file = pathname(ftp->cd,arg);
  520.         if(!permcheck(ftp,MKD_CMD,file)){
  521.             tprintf(ftp->control,noperm);
  522.         } else if(mkdir(file,0777) == 0){
  523.             tprintf(ftp->control,mkdok);
  524.         } else {
  525.             tprintf(ftp->control,cantmake);
  526.         }
  527.         free(file);
  528.         break;
  529.     case XRMD_CMD:
  530.     case RMD_CMD:
  531.         file = pathname(ftp->cd,arg);
  532.         if(!permcheck(ftp,RMD_CMD,file)){
  533.              tprintf(ftp->control,noperm);
  534.         } else if(rmdir(file) == 0){
  535.             tprintf(ftp->control,deleok);
  536.         } else {
  537.             tprintf(ftp->control,delefail);
  538.         }
  539.         free(file);
  540.         break;
  541.     case STRU_CMD:
  542.         if(tolower(arg[0]) != 'f')
  543.             tprintf(ftp->control,unsupp);
  544.         else
  545.             tprintf(ftp->control,okay);
  546.         break;
  547.     case MODE_CMD:
  548.         if(tolower(arg[0]) != 's')
  549.             tprintf(ftp->control,unsupp);
  550.         else
  551.             tprintf(ftp->control,okay);
  552.         break;
  553.     }
  554. #endif
  555. }
  556. static
  557. int
  558. pport(sock,arg)
  559. struct socket *sock;
  560. char *arg;
  561. {
  562.     int32 n;
  563.     int atoi(),i;
  564.  
  565.     n = 0;
  566.     for(i=0;i<4;i++){
  567.         n = atoi(arg) + (n << 8);
  568.         if((arg = index(arg,',')) == NULLCHAR)
  569.             return -1;
  570.         arg++;
  571.     }
  572.     sock->address = n;
  573.     n = atoi(arg);
  574.     if((arg = index(arg,',')) == NULLCHAR)
  575.         return -1;
  576.     arg++;
  577.     n = atoi(arg) + (n << 8);
  578.     sock->port = n;
  579.     return 0;
  580. }
  581. /* Attempt to log in the user whose name is in ftp->username and password
  582.  * in pass
  583.  */
  584. static
  585. ftplogin(ftp,pass)
  586. struct ftp *ftp;
  587. char *pass;
  588. {
  589.     char buf[80],*cp,*cp1;
  590.     FILE *fp;
  591.     int anony = 0;
  592.  
  593.     if((fp = fopen(userfile,"r")) == NULLFILE){
  594.         /* Userfile doesn't exist */
  595.         tprintf(ftp->control,noperm);
  596.         return;
  597.     }
  598.     while(fgets(buf,sizeof(buf),fp),!feof(fp)){
  599.         if(buf[0] == '#')
  600.             continue;    /* Comment */
  601.         if((cp = index(buf,' ')) == NULLCHAR)
  602.             /* Bogus entry */
  603.             continue;
  604.         *cp++ = '\0';        /* Now points to password */
  605.         if(strcmp(ftp->username,buf) == 0)
  606.             break;        /* Found user name */
  607.     }
  608.     if(feof(fp)){
  609.         /* User name not found in file */
  610.         fclose(fp);
  611.         tprintf(ftp->control,noperm);
  612.         return;
  613.     }
  614.     fclose(fp);
  615.     /* Look for space after password field in file */
  616.     if((cp1 = index(cp,' ')) == NULLCHAR){
  617.         /* Invalid file entry */
  618.         tprintf(ftp->control,noperm);
  619.         return;
  620.     }
  621.     *cp1++ = '\0';    /* Now points to path field */
  622.     if(strcmp(cp,"*") == 0)
  623.         anony = 1;    /* User ID is password-free */
  624.     if(!anony && strcmp(cp,pass) != 0){
  625.         /* Password required, but wrong one given */
  626.         tprintf(ftp->control,noperm);
  627.         return;
  628.     }
  629.     if((cp = index(cp1,' ')) == NULLCHAR){
  630.         /* Permission field missing */
  631.         tprintf(ftp->control,noperm);
  632.         return;
  633.     }
  634.     *cp++ = '\0';    /* now points to permission field */
  635.  
  636.     /* Set up current directory and path prefix */
  637.     ftp->cd = malloc((unsigned)strlen(cp1)+1);
  638.     ftp->path = malloc((unsigned)strlen(cp1)+1);
  639.     strcpy(ftp->cd,cp1);
  640.     strcpy(ftp->path,cp1);
  641.     
  642.     /* And finally set the permission bits */
  643.     ftp->perms = atoi(cp);
  644.  
  645.     tprintf(ftp->control,logged);
  646.     if(!anony)
  647.         log(ftp->control,"%s logged in",ftp->username);
  648.     else
  649.         log(ftp->control,"%s logged in, ID %s",ftp->username,pass);
  650. }        
  651.  
  652. #ifdef    MSDOS
  653. /* Illegal characters in a DOS filename */
  654. char badchars[] = "\"[]:|<>+=;,";
  655. #endif
  656.  
  657. /* Return 1 if the file operation is allowed, 0 otherwise */
  658. permcheck(ftp,op,file)
  659. struct ftp *ftp;
  660. int op;
  661. char *file;
  662. {
  663.     char *cp;
  664.  
  665.     if(file == NULLCHAR || ftp->path == NULLCHAR)
  666.         return 0;    /* Probably hasn't logged in yet */
  667. #ifdef    MSDOS
  668.     /* Check for characters illegal in MS-DOS file names */
  669.     for(cp = badchars;*cp != '\0';cp++){
  670.         if(index(file,*cp) != NULLCHAR)
  671.             return 0;    
  672.     }
  673. #endif
  674. #if    (defined(AMIGA) || defined(MAC))
  675. #else
  676.     /* The target file must be under the user's allowed search path */
  677.     if(strncmp(file,ftp->path,strlen(ftp->path)) != 0)
  678.         return 0;
  679. #endif
  680.  
  681.     switch(op){
  682.     case RETR_CMD:
  683.         /* User must have permission to read files */
  684.         if(ftp->perms & FTP_READ)
  685.             return 1;
  686.         return 0;
  687.     case DELE_CMD:
  688.     case RMD_CMD:
  689.         /* User must have permission to (over)write files */
  690.         if(ftp->perms & FTP_WRITE)
  691.             return 1;
  692.         return 0;
  693.     case STOR_CMD:
  694.     case MKD_CMD:
  695.         /* User must have permission to (over)write files, or permission
  696.          * to create them if the file doesn't already exist
  697.          */
  698.         if(ftp->perms & FTP_WRITE)
  699.             return 1;
  700.         if(access(file,2) == -1 && (ftp->perms & FTP_CREATE))
  701.             return 1;
  702.         return 0;
  703.     }
  704.     return 0;    /* "can't happen" -- keep lint happy */
  705. }
  706.